package com.sqlcheck.service.impl;

import com.sqlcheck.model.SqlStatement;
import com.sqlcheck.service.MybatisXmlAnalysisService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.w3c.dom.*;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Stream;

@Slf4j
@Service
@RequiredArgsConstructor
public class MybatisXmlAnalysisServiceImpl implements MybatisXmlAnalysisService {

    // 新增依赖注入
    private final MybatisSqlContentProcessor sqlContentProcessor;

    // 只提取可执行SQL标签
    private static final Set<String> EXECUTABLE_SQL_TAGS = Set.of(
        "select", "insert", "update", "delete"
    );

    // 完全忽略的配置标签
    private static final Set<String> IGNORED_TAGS = Set.of(
        // SQL片段和重用
        "sql",
        
        // 结果映射相关
        "resultMap", "collection", "association", "id", "result", 
        "constructor", "idArg", "arg", "discriminator", "case",
        
        // 参数映射相关
        "parameterMap", "parameter",
        
        // 缓存相关
        "cache", "cache-ref",
        
        // 根节点和配置
        "mapper", "configuration", "properties", "settings",
        "typeAliases", "typeAlias", "typeHandlers", "typeHandler",
        "objectFactory", "objectWrapperFactory", "reflectorFactory",
        "plugins", "plugin", "environments", "environment",
        "transactionManager", "dataSource", "databaseIdProvider", "mappers"
    );

    // 动态SQL标签列表（用于内容处理）
    private static final Set<String> DYNAMIC_SQL_TAGS = Set.of(
        "if", "choose", "when", "otherwise", "where", "set", "trim", "foreach", "bind"
    );

    @Override
    public List<SqlStatement> analyzeXmlFile(Path xmlFilePath) {
        List<SqlStatement> sqlStatements = new ArrayList<>();

        try {
            if (!Files.exists(xmlFilePath)) {
                log.warn("XML文件不存在: {}", xmlFilePath);
                return sqlStatements;
            }

            if (!isMybatisXmlFile(xmlFilePath)) {
                log.debug("跳过非MyBatis XML文件: {}", xmlFilePath);
                return sqlStatements;
            }

            log.debug("开始分析MyBatis XML文件: {}", xmlFilePath);

            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            factory.setValidating(false);
            factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(xmlFilePath.toFile());

            // 提取SQL语句
            sqlStatements.addAll(extractSqlFromDocument(document, xmlFilePath));

            log.info("XML文件分析完成，提取到 {} 条SQL语句: {}", sqlStatements.size(), xmlFilePath);

        } catch (ParserConfigurationException | SAXException | IOException e) {
            log.error("解析XML文件失败: " + xmlFilePath, e);
        }

        return sqlStatements;
    }

    @Override
    public List<SqlStatement> analyzeXmlDirectory(Path directoryPath) {
        List<SqlStatement> allSqlStatements = new ArrayList<>();

        try {
            if (!Files.exists(directoryPath) || !Files.isDirectory(directoryPath)) {
                log.warn("目录不存在或不是目录: {}", directoryPath);
                return allSqlStatements;
            }

            log.info("开始分析目录下的MyBatis XML文件: {}", directoryPath);

            try (Stream<Path> paths = Files.walk(directoryPath)) {
                paths.filter(Files::isRegularFile)
                        .filter(path -> path.toString().toLowerCase().endsWith(".xml"))
                        .forEach(xmlPath -> {
                            List<SqlStatement> statements = analyzeXmlFile(xmlPath);
                            allSqlStatements.addAll(statements);
                        });
            }

            log.info("目录分析完成，共提取到 {} 条SQL语句", allSqlStatements.size());

        } catch (IOException e) {
            log.error("遍历目录失败: " + directoryPath, e);
        }

        return allSqlStatements;
    }

    @Override
    public boolean isMybatisXmlFile(Path xmlFilePath) {
        try {
            if (!Files.exists(xmlFilePath) || !xmlFilePath.toString().toLowerCase().endsWith(".xml")) {
                return false;
            }

            // 读取文件前几行检查是否包含MyBatis相关内容
            List<String> lines = Files.readAllLines(xmlFilePath);
            String content = String.join("\n", lines.subList(0, Math.min(10, lines.size())));

            // 更宽松的MyBatis检测逻辑
            boolean isMybatis = content.contains("mybatis") ||
                    content.contains("mapper") ||
                    content.contains("<!DOCTYPE mapper") ||
                    content.contains("http://mybatis.org/dtd/") ||
                    content.contains("<select") ||
                    content.contains("<insert") ||
                    content.contains("<update") ||
                    content.contains("<delete") ||
                    content.contains("<sql") ||
                    content.contains("<resultMap") ||
                    content.contains("<parameterMap");

            if (isMybatis) {
                log.debug("检测到MyBatis XML文件: {}", xmlFilePath);
            } else {
                log.debug("跳过非MyBatis XML文件: {}", xmlFilePath);
            }

            return isMybatis;

        } catch (IOException e) {
            log.debug("检查文件失败: " + xmlFilePath, e);
            return false;
        }
    }

    private List<SqlStatement> extractSqlFromDocument(Document document, Path sourceFile) {
        List<SqlStatement> sqlStatements = new ArrayList<>();

        // 读取原始文件内容用于精确行号计算
        List<String> fileLines = null;
        try {
            fileLines = Files.readAllLines(sourceFile);
        } catch (IOException e) {
            log.warn("无法读取XML文件内容用于行号计算: {}", sourceFile, e);
        }

        // 只获取可执行的SQL标签
        for (String tagName : EXECUTABLE_SQL_TAGS) {
            NodeList nodes = document.getElementsByTagName(tagName);
            for (int i = 0; i < nodes.getLength(); i++) {
                Element element = (Element) nodes.item(i);
                SqlStatement statement = extractSqlFromElement(element, sourceFile, tagName, fileLines);
                if (statement != null) {
                    sqlStatements.add(statement);
                }
            }
        }

        return sqlStatements;
    }

    private SqlStatement extractSqlFromElement(Element element, Path sourceFile, String tagName,
            List<String> fileLines) {
        try {
            String sqlContent = extractTextContent(element);
            if (sqlContent == null || sqlContent.trim().isEmpty()) {
                return null;
            }

            // 清理SQL内容
            sqlContent = cleanSqlContent(sqlContent);

            return SqlStatement.builder()
                    .content(sqlContent)
                    .sourceFile(sourceFile.toString())
                    .sourceType(SqlStatement.SourceType.MYBATIS_XML)
                    .sqlType(determineSqlTypeFromTag(tagName))
                    .lineNumber(getElementLineNumber(element, tagName, fileLines))
                    .build();

        } catch (Exception e) {
            log.error("提取SQL语句失败: " + sourceFile, e);
            return null;
        }
    }

    /**
     * 最大化提取元素文本内容，包含所有动态分支
     */
    private String extractTextContent(Element element) {
        StringBuilder content = new StringBuilder();
        extractAllContentRecursively(element, content);
        return content.toString();
    }

    /**
     * 递归提取所有内容，宽容解析
     */
    private void extractAllContentRecursively(Node node, StringBuilder collector) {
        if (node.getNodeType() == Node.TEXT_NODE) {
            String text = node.getTextContent();
            if (text != null && !text.trim().isEmpty()) {
                collector.append(" ").append(text.trim()).append(" ");
            }
        } else if (node.getNodeType() == Node.CDATA_SECTION_NODE) {
            collector.append(" ").append(node.getTextContent()).append(" ");
        } else if (node.getNodeType() == Node.ELEMENT_NODE) {
            Element element = (Element) node;
            String tagName = element.getTagName().toLowerCase();
            
            // 处理动态SQL标签
            if (DYNAMIC_SQL_TAGS.contains(tagName)) {
                processSpecialTag(element, tagName, collector);
            }
            
            // 递归处理所有子节点
            NodeList children = node.getChildNodes();
            for (int i = 0; i < children.getLength(); i++) {
                extractAllContentRecursively(children.item(i), collector);
            }
        }
    }

    /**
     * 处理特殊动态标签，最大化包含内容
     * 遵循宽容解析原则：提取所有可能的SQL内容，不添加注释标记
     */
    private void processSpecialTag(Element element, String tagName, StringBuilder collector) {
        switch (tagName) {
            case "where":
                collector.append(" WHERE ");
                break;
            case "set": 
                collector.append(" SET ");
                break;
            case "trim":
                // trim标签：直接包含内容，让其他属性处理逻辑在应用层处理
                collector.append(" ");
                break;
            case "choose":
                // choose标签：包含所有when和otherwise分支，不添加注释
                collector.append(" ");
                break;
            case "when":
                // when分支：无条件包含内容
                collector.append(" ");
                break;
            case "otherwise":
                // otherwise分支：无条件包含内容
                collector.append(" ");
                break;
            case "if":
                // if标签：无条件包含内容，不添加任何标记
                collector.append(" ");
                break;
            case "foreach":
                // foreach：包含循环体模板内容
                collector.append(" ");
                break;
            case "bind":
                // bind标签：用于变量绑定，不输出SQL内容
                // 不添加任何内容到collector
                break;
            default:
                collector.append(" ");
        }
    }

    /**
     * 简化的SQL清理，遵循宽容原则，集成通用处理器
     */
    private String cleanSqlContent(String sqlContent) {
        if (sqlContent == null) {
            return null;
        }

        try {
            // 使用通用处理器
            if (sqlContentProcessor.needsComplexProcessing(sqlContent)) {
                return sqlContentProcessor.processComplexSql(sqlContent, "XML");
            }
            
            // 简单清理 - 使用更安全的正则表达式
            sqlContent = sqlContent.replaceAll("\\s+", " ").trim();
            
            // 安全的参数占位符处理
            sqlContent = cleanParameterPlaceholders(sqlContent);
            
            return sqlContent;
            
        } catch (Exception e) {
            log.warn("SQL内容清理失败，使用基础清理: {}, SQL前100字符: {}", 
                     e.getMessage(), 
                     sqlContent.length() > 100 ? sqlContent.substring(0, 100) + "..." : sqlContent);
            
            // 降级策略：只做最基础的空白清理
            return sqlContent.replaceAll("\\s+", " ").trim();
        }
    }
    
    /**
     * 安全的参数占位符清理
     */
    private String cleanParameterPlaceholders(String sqlContent) {
        try {
            // 分步处理，避免复杂正则表达式问题
            // 1. 处理 #{ } 参数
            if (sqlContent.contains("#{")) {
                sqlContent = sqlContent.replaceAll("\\s*#\\{", " #{");
            }
            if (sqlContent.contains("}")) {
                sqlContent = sqlContent.replaceAll("\\}\\s*", "} ");
            }
            
            // 2. 处理 ${ } 参数 - 使用更安全的方式
            if (sqlContent.contains("${")) {
                sqlContent = sqlContent.replaceAll("\\s*\\$\\{", " ${");
            }
            
            return sqlContent;
        } catch (Exception e) {
            log.debug("参数占位符清理失败，跳过此步骤: {}", e.getMessage());
            return sqlContent;
        }
    }

    private SqlStatement.SqlType determineSqlTypeFromTag(String tagName) {
        switch (tagName.toLowerCase()) {
            case "select":
                return SqlStatement.SqlType.QUERY;
            case "insert":
            case "update":
            case "delete":
                return SqlStatement.SqlType.DML;
            default:
                return SqlStatement.SqlType.QUERY;
        }
    }

    private Integer getElementLineNumber(Element element, String tagName, List<String> fileLines) {
        // 尝试获取行号，这个功能需要特殊的XML解析器支持
        Object userData = element.getUserData("lineNumber");
        if (userData instanceof Integer) {
            return (Integer) userData;
        }

        // 如果有文件内容，尝试通过内容匹配查找行号
        if (fileLines != null) {
            return findElementLineByContent(element, tagName, fileLines);
        }

        // 如果无法获取精确行号，返回估算值
        return estimateLineNumber(element);
    }

    /**
     * 通过内容匹配查找元素行号
     */
    private Integer findElementLineByContent(Element element, String tagName, List<String> fileLines) {
        try {
            // 获取元素的id属性或其他标识属性
            String elementId = element.getAttribute("id");
            String elementNamespace = element.getAttribute("namespace");

            // 构建搜索模式
            List<String> searchPatterns = new ArrayList<>();

            if (elementId != null && !elementId.trim().isEmpty()) {
                // 优先通过id属性匹配
                searchPatterns.add("<" + tagName + "[^>]*id\\s*=\\s*[\"']" + elementId + "[\"'][^>]*>");
                searchPatterns.add("<" + tagName + "\\s+id\\s*=\\s*[\"']" + elementId + "[\"']");
            }

            // 添加通用标签匹配模式
            searchPatterns.add("<" + tagName + "\\s");
            searchPatterns.add("<" + tagName + ">");

            // 逐行搜索匹配
            for (int i = 0; i < fileLines.size(); i++) {
                String line = fileLines.get(i);
                for (String pattern : searchPatterns) {
                    if (line.matches(".*" + pattern + ".*")) {
                        // 找到匹配行，验证是否确实是我们要找的元素
                        if (validateElementMatch(element, line, tagName, elementId)) {
                            log.debug("通过内容匹配找到元素行号: 标签={}, id={}, 行号={}", tagName, elementId, i + 1);
                            return i + 1; // 行号从1开始
                        }
                    }
                }
            }

            log.debug("无法通过内容匹配找到元素行号: 标签={}, id={}", tagName, elementId);

        } catch (Exception e) {
            log.debug("通过内容匹配查找行号失败", e);
        }

        return null;
    }

    /**
     * 验证元素匹配是否正确
     */
    private boolean validateElementMatch(Element element, String line, String tagName, String elementId) {
        // 基本验证：行包含标签名
        if (!line.contains("<" + tagName)) {
            return false;
        }

        // 如果有id属性，验证id是否匹配
        if (elementId != null && !elementId.trim().isEmpty()) {
            return line.contains("id") && line.contains(elementId);
        }

        // 如果没有特定的id，认为第一个匹配就是正确的（可能需要进一步优化）
        return true;
    }

    private Integer estimateLineNumber(Element element) {
        // 简单的行号估算，基于元素在文档中的位置
        int position = 1;
        Node current = element;

        while (current != null) {
            Node previous = current.getPreviousSibling();
            while (previous != null) {
                if (previous.getNodeType() == Node.TEXT_NODE) {
                    position += countLines(previous.getTextContent());
                } else if (previous.getNodeType() == Node.ELEMENT_NODE) {
                    position += 1; // 简化计算，每个元素算一行
                }
                previous = previous.getPreviousSibling();
            }
            current = current.getParentNode();
        }

        return position;
    }

    private int countLines(String text) {
        if (text == null || text.isEmpty()) {
            return 0;
        }
        return (int) text.chars().filter(ch -> ch == '\n').count();
    }
}